﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;

namespace Red.Controls.Diagrams.Declarators
{
    public class NodeDeclarators
    {
        #region Data

        private Dictionary<string, NodeDeclarator> _classDeclarators = new Dictionary<string, NodeDeclarator>();
        private readonly Dictionary<NodeDeclarator, List<NodeDeclarator>> _inheritance = new Dictionary<NodeDeclarator, List<NodeDeclarator>>();  

        #endregion Data

        #region Public methods

        public void Load( string filePath )
        {
            var declarators = LoadDeclaratorFile( filePath );
            _classDeclarators = declarators.ToDictionary( item => item.Class );
            ResolveInheritance();
        }

        public void Save( string filePath, IEnumerable<NodeDeclarator> declarators )
        {
            SaveDeclaratorFile( filePath, declarators );
        }

        public NodeDeclarator GetNodeDeclarator( string className )
        {
            NodeDeclarator nodeDeclarator;
            if ( string.IsNullOrEmpty( className ) )
                return null;

            _classDeclarators.TryGetValue( className, out nodeDeclarator );
            return nodeDeclarator;
        }

        public IEnumerable<NodeDeclarator> GetIheritedFrom( NodeDeclarator declarator )
        {
            return _inheritance[ declarator ];
        }

        public IEnumerable<NodeDeclarator> GetDeclarators()
        {
            return _classDeclarators.Values;
        }

        public IEnumerable<NodeDeclarator> GetDeclarators( string baseClassName )
        {
            return _inheritance.FirstOrDefault( item => item.Key.Class == baseClassName ).Value;
        }

        #endregion Public methods

        #region Private methods

        private void ResolveInheritance()
        {
            foreach ( var declarator in _classDeclarators.Values )
            {
                _inheritance.Add( declarator, new List<NodeDeclarator> { declarator } );
                var baseClass = declarator.BaseClass;
                declarator.Base = GetNodeDeclarator( baseClass );
            }

            foreach ( var declarator in _inheritance.Keys )
            {
                var baseDeclarator = declarator.Base;
                if ( baseDeclarator == null )
                    continue;

                AddToInheritanceDictionary( declarator.Base, declarator );
            }
        }

        private void AddToInheritanceDictionary( NodeDeclarator baseDeclarator, NodeDeclarator declarator )
        {
            while ( true )
            {
                if ( baseDeclarator == null )
                    return;

                _inheritance[ baseDeclarator ].Add( declarator );
                baseDeclarator = baseDeclarator.Base;
            }
        }

        private IEnumerable<NodeDeclarator> LoadDeclaratorFile( string filePath )
        {
            var serializer = new XmlSerializer( typeof ( NodeDeclaratorFile ) );
            var fileStream = new FileStream( filePath, FileMode.Open, FileAccess.Read );
            var nodeDeclaratorFile = ( NodeDeclaratorFile )serializer.Deserialize( fileStream );
            return nodeDeclaratorFile.Nodes;
        }

        private static void SaveDeclaratorFile( string filePath, IEnumerable<NodeDeclarator> declarators )
        {
            var file = new NodeDeclaratorFile { Nodes = declarators.ToList() };
            var emptyNamespaces = new XmlSerializerNamespaces( new[] { XmlQualifiedName.Empty } );
            var settings = new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true };
            var serializer = new XmlSerializer( typeof ( NodeDeclaratorFile ) );
            using ( var stream = new StreamWriter( filePath ) )
            using ( var writer = XmlWriter.Create( stream, settings ) )
            {
                serializer.Serialize( writer, file, emptyNamespaces );
            }
        }

        #endregion Private methods
    }         
}
